var filterVertexShaderStr = "\
attribute vec2 inPosition;\
attribute vec2 inTexCoord0;\
varying vec2 outTexCoord0;\
void main(void)\
{\
  gl_Position = vec4(inPosition,0.0,1.0);\
  outTexCoord0 = inTexCoord0;\
}";
var filterFragmentShaderStr = "\
varying vec2 outTexCoord0;\
uniform sampler2D texture;\
uniform sampler2D depthTexture;\
uniform int  levelFilterEnable;\
uniform vec3 levelFilterParam0;\
uniform vec2 levelFilterParam1;\
uniform int  fogMode;\
uniform vec3 fogColor;\
uniform mat4 fogInverseViewProjection;\
uniform vec3 fogParam;\
uniform vec2 fogMinMaxFactor;\
uniform vec2 fogLayeredParam;\
vec3 levelFilter(vec3 srcColor){\
  return (pow( (((srcColor * 255.0) - levelFilterParam0.x) / (levelFilterParam0.y - levelFilterParam0.x)),levelFilterParam0.z) * (levelFilterParam1.y - levelFilterParam1.x) + levelFilterParam1.x) / 255.0;\
}\
vec3 fog(vec3 fragColor,float depth){\
  float fogFactor;\
  if(fogMode==1){\
    float z = -fogParam.y/(depth.x+fogParam.x);\;\
    fogFactor = exp2(fogParam.z*z*z);\
  }\
  else if(fogMode==2){\
    float zOverW = depth*2.0 - 1.0;\
    vec4 vec4H = vec4(outTexCoord0 * 2.0 - 1.0, zOverW, 1.0);\
    vec4 vec4D = fogInverseViewProjection * vec4H;\
    vec4 fragPos = vec4D / vec4D.w;\
    float f =  max((fragPos.y-fogLayeredParam.x)*fogLayeredParam.y,0.0);\
    fogFactor = exp2(fogParam.z*f*f);\
  }\
  fogFactor = clamp(fogFactor,fogMinMaxFactor.x,fogMinMaxFactor.y);\
  return mix(fogColor, fragColor, fogFactor );\
 }\
void main(void)\
{\
  vec3 srcColor  = texture2D(texture, outTexCoord0.st);\
  vec3 filterColor = srcColor;\
  if(levelFilterEnable==1)\
    filterColor = levelFilter(filterColor);\
  if(fogMode!=0){\
    float depth = texture2D(depthTexture,outTexCoord0.st).x;\
    filterColor = fog(filterColor,depth);\
  }\
  gl_FragColor = vec4(filterColor,1.0 );\
}";
function gewFilter(game,gltexture,gl_depth_texture,
                   outWidth,outHeight,
                   inWidth,inHeight)
{
  // General properties
  this.game = game;
  this.gl = game.gl;
  var gl = game.gl;
  this.srcGLTexture = gltexture;// the texture to be filtering
  this.srcGLDepthTexture = gl_depth_texture;
  //this.name = "PhongEffect";
  //this.scene3d = null;//Current scene3d that using this effect 
  //this.materialList = {};
  //this.currentPass = null;
   
  // Shader properties
  this.vs = createVertexShader(gl,filterVertexShaderStr);
  this.fs = createFragmentShader(gl,filterFragmentShaderStr);
  var program = gl.createProgram();
  gl.attachShader(program,this.vs);
  gl.attachShader(program,this.fs);
  gl.linkProgram(program);
  checkProgramLinkStatus(gl,program);
  this.program = program;
  // Shader param id
  this.inPosition = gl.getAttribLocation(program,"inPosition");
  this.inTexCoord0 = gl.getAttribLocation(program,"inTexCoord0");
  this.texture = gl.getUniformLocation(program, "texture");
  this.depthTexture = gl.getUniformLocation(program, "depthTexture");
  // Level Filter parameter
  this.levelFilterEnable = gl.getUniformLocation(program, "levelFilterEnable");
  this.levelFilterParam0 = gl.getUniformLocation(program, "levelFilterParam0");
  this.levelFilterParam1 = gl.getUniformLocation(program, "levelFilterParam1");
  
  // Fog parameter
  this.fogMode = gl.getUniformLocation(program, "fogMode");
  this.fogColor = gl.getUniformLocation(program, "fogColor");
  this.fogInverseViewProjection = gl.getUniformLocation(program, "fogInverseViewProjection");
  //this.fogViewPosition = gl.getUniformLocation(program, "fogViewPosition");
  this.fogParam = gl.getUniformLocation(program, "fogParam");
  this.fogMinMaxFactor = gl.getUniformLocation(program, "fogMinMaxFactor");
  this.fogLayeredParam = gl.getUniformLocation(program, "fogLayeredParam");
  // Create buffer
  var vertices = new WebGLFloatArray( [-1,-1,-1,1,1,1,
                                       -1,-1,1,-1,1,1]);
  var maxu = outWidth/inWidth;
  var maxv = outHeight/inHeight;
  var texcoords = new WebGLFloatArray([0,0,0,maxv,maxu,maxv,
                                       0,0,maxu,0,maxu,maxv]);                              
  var ver_buff = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, ver_buff);
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  var tex_buff = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, tex_buff);
  gl.bufferData(gl.ARRAY_BUFFER, texcoords, gl.STATIC_DRAW);
  this.glBuffers = {};
  this.glBuffers.positions = ver_buff;
  this.glBuffers.texcoords = tex_buff;
  
  
  this.levelFilter = {};
  this.levelFilter.inBlack = 0.0;
  this.levelFilter.inWhite = 255.0;
  this.levelFilter.inGamma = 1.0;
  this.levelFilter.outBlack = 0.0;
  this.levelFilter.outWhite = 255.0;
  this.levelFilter.isEnable = true;
}
gewFilter.prototype.render = function(scene3d) 
{
  var gl = this.gl; 
  this.scene3d = scene3d;
  // push GL state
  // set GL state
  gl.useProgram(this.program);
  gl.enableVertexAttribArray(this.inPosition);
  gl.enableVertexAttribArray(this.inTexCoord0);
  var levelFilter = this.levelFilter;
  ////////////////////////////////////////////////////
  // LevelFilter
  if(levelFilter.isEnable)
  {
    gl.uniform1i(this.levelFilterEnable, 1);
    gl.uniform3f(this.levelFilterParam0, levelFilter.inBlack, levelFilter.inWhite, 1.0/levelFilter.inGamma);
    gl.uniform2f(this.levelFilterParam1, levelFilter.outBlack, levelFilter.outWhite);
  }
  else  
    gl.uniform1i(this.levelFilterEnable, 0);
  
  //////////////////////////////////////////////////////
  // Fog
  var fog = scene3d.fog;
  var fogMode = fog.mode;
  gl.uniform1i(this.fogMode,fogMode);
  var fogColor = fog.color;
  var fogDensity = fog.density;
  var activeCam = scene3d.camera_system.getActiveCamera();
  var viewPos = scene3d.viewPosition;
  if(fogMode!=0)
  {
    var n = activeCam.getNear();
    var f = activeCam.getFar();
    var p33 = (f+n)/(n-f);
    var p34 = (-2*f*n)/(f-n);
    var negDensitySquareMulLO2 = -fogDensity*fogDensity*1.442695;
    gl.uniform3f(this.fogParam,p33,p34,negDensitySquareMulLO2);
    gl.uniform3f(this.fogColor,fogColor.x,fogColor.y,fogColor.z);
    gl.uniform2f(this.fogMinMaxFactor,fog.minFactor,fog.maxFactor);
    // Homogeneous Fog
    if( fogMode==1)
    {      
      //gl.uniform3f(this.viewPosition, viewPos.x,viewPos.y,viewPos.z);
    }
    // Layered Fog
    else if(fogMode==2)
    {
      var inverseViewProjectMat = scene3d.inverseViewProjectMat;
      gl.uniformMatrix4fv(this.fogInverseViewProjection, false, inverseViewProjectMat.data);
      gl.uniform2f(this.fogLayeredParam,fog.layeredHeightOffset,fog.layeredFactor);
    }
  }
  
  gl.uniform1i(this.texture, 0);
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, this.srcGLTexture);
  
  gl.uniform1i(this.depthTexture, 1);
  gl.activeTexture(gl.TEXTURE1);
  gl.bindTexture(gl.TEXTURE_2D, this.srcGLDepthTexture);
  
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.positions);
  gl.vertexAttribPointer(this.inPosition, 2, gl.FLOAT, false, 0, 0);
  gl.bindBuffer(gl.ARRAY_BUFFER, this.glBuffers.texcoords);
  gl.vertexAttribPointer(this.inTexCoord0, 2, gl.FLOAT, false, 0, 0);
  gl.drawArrays(gl.TRIANGLES, 0, 6);
  gl.disableVertexAttribArray(this.inPosition);
  gl.disableVertexAttribArray(this.inTexCoord0);
}
